Add TAL-Reverb-II plugin to test
[juce-lv2.git] / juce / source / extras / audio plugin host / Source / GraphEditorPanel.cpp
blob6ed7eae717259b6433e6fb488844907f85a4cd63
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-9 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../JuceLibraryCode/JuceHeader.h"
27 #include "GraphEditorPanel.h"
28 #include "InternalFilters.h"
29 #include "MainHostWindow.h"
32 //==============================================================================
33 class PluginWindow;
34 static Array <PluginWindow*> activePluginWindows;
36 PluginWindow::PluginWindow (Component* const uiComp,
37 AudioProcessorGraph::Node* owner_,
38 const bool isGeneric_)
39 : DocumentWindow (uiComp->getName(), Colours::lightblue,
40 DocumentWindow::minimiseButton | DocumentWindow::closeButton),
41 owner (owner_),
42 isGeneric (isGeneric_)
44 setSize (400, 300);
46 setContentOwned (uiComp, true);
48 setTopLeftPosition (owner->properties.getWithDefault ("uiLastX", Random::getSystemRandom().nextInt (500)),
49 owner->properties.getWithDefault ("uiLastY", Random::getSystemRandom().nextInt (500)));
50 setVisible (true);
52 activePluginWindows.add (this);
55 void PluginWindow::closeCurrentlyOpenWindowsFor (const uint32 nodeId)
57 for (int i = activePluginWindows.size(); --i >= 0;)
58 if (activePluginWindows.getUnchecked(i)->owner->nodeId == nodeId)
59 delete activePluginWindows.getUnchecked(i);
62 void PluginWindow::closeAllCurrentlyOpenWindows()
64 for (int i = activePluginWindows.size(); --i >= 0;)
65 delete activePluginWindows.getUnchecked(i);
68 PluginWindow* PluginWindow::getWindowFor (AudioProcessorGraph::Node* node,
69 bool useGenericView)
71 for (int i = activePluginWindows.size(); --i >= 0;)
72 if (activePluginWindows.getUnchecked(i)->owner == node
73 && activePluginWindows.getUnchecked(i)->isGeneric == useGenericView)
74 return activePluginWindows.getUnchecked(i);
76 AudioProcessorEditor* ui = nullptr;
78 if (! useGenericView)
80 ui = node->getProcessor()->createEditorIfNeeded();
82 if (ui == nullptr)
83 useGenericView = true;
86 if (useGenericView)
88 ui = new GenericAudioProcessorEditor (node->getProcessor());
91 if (ui != nullptr)
93 AudioPluginInstance* const plugin = dynamic_cast <AudioPluginInstance*> (node->getProcessor());
95 if (plugin != nullptr)
96 ui->setName (plugin->getName());
98 return new PluginWindow (ui, node, useGenericView);
101 return nullptr;
104 PluginWindow::~PluginWindow()
106 activePluginWindows.removeValue (this);
107 clearContentComponent();
110 void PluginWindow::moved()
112 owner->properties.set ("uiLastX", getX());
113 owner->properties.set ("uiLastY", getY());
116 void PluginWindow::closeButtonPressed()
118 delete this;
121 //==============================================================================
122 class PinComponent : public Component,
123 public SettableTooltipClient
125 public:
126 PinComponent (FilterGraph& graph_,
127 const uint32 filterID_, const int index_, const bool isInput_)
128 : filterID (filterID_),
129 index (index_),
130 isInput (isInput_),
131 graph (graph_)
133 const AudioProcessorGraph::Node::Ptr node (graph.getNodeForId (filterID_));
135 if (node != nullptr)
137 String tip;
139 if (isInput)
140 tip = node->getProcessor()->getInputChannelName (index_);
141 else
142 tip = node->getProcessor()->getOutputChannelName (index_);
144 if (tip.isEmpty())
146 if (index_ == FilterGraph::midiChannelNumber)
147 tip = isInput ? "Midi Input" : "Midi Output";
148 else
149 tip = (isInput ? "Input " : "Output ") + String (index_ + 1);
152 setTooltip (tip);
155 setSize (16, 16);
158 void paint (Graphics& g)
160 const float w = (float) getWidth();
161 const float h = (float) getHeight();
163 Path p;
164 p.addEllipse (w * 0.25f, h * 0.25f, w * 0.5f, h * 0.5f);
166 p.addRectangle (w * 0.4f, isInput ? (0.5f * h) : 0.0f, w * 0.2f, h * 0.5f);
168 g.setColour (index == FilterGraph::midiChannelNumber ? Colours::red : Colours::green);
169 g.fillPath (p);
172 void mouseDown (const MouseEvent& e)
174 getGraphPanel()->beginConnectorDrag (isInput ? 0 : filterID,
175 index,
176 isInput ? filterID : 0,
177 index,
181 void mouseDrag (const MouseEvent& e)
183 getGraphPanel()->dragConnector (e);
186 void mouseUp (const MouseEvent& e)
188 getGraphPanel()->endDraggingConnector (e);
191 const uint32 filterID;
192 const int index;
193 const bool isInput;
195 private:
196 FilterGraph& graph;
198 GraphEditorPanel* getGraphPanel() const noexcept
200 return findParentComponentOfClass ((GraphEditorPanel*) nullptr);
203 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PinComponent);
206 //==============================================================================
207 class FilterComponent : public Component
209 public:
210 FilterComponent (FilterGraph& graph_,
211 const uint32 filterID_)
212 : graph (graph_),
213 filterID (filterID_),
214 numInputs (0),
215 numOutputs (0),
216 pinSize (16),
217 font (13.0f, Font::bold),
218 numIns (0),
219 numOuts (0)
221 shadow.setShadowProperties (2.5f, 0.5f, -1, 0);
222 setComponentEffect (&shadow);
224 setSize (150, 60);
227 ~FilterComponent()
229 deleteAllChildren();
232 void mouseDown (const MouseEvent& e)
234 originalPos = localPointToGlobal (Point<int>());
236 toFront (true);
238 if (e.mods.isPopupMenu())
240 PopupMenu m;
241 m.addItem (1, "Delete this filter");
242 m.addItem (2, "Disconnect all pins");
243 m.addSeparator();
244 m.addItem (3, "Show plugin UI");
245 m.addItem (4, "Show all parameters");
247 const int r = m.show();
249 if (r == 1)
251 graph.removeFilter (filterID);
252 return;
254 else if (r == 2)
256 graph.disconnectFilter (filterID);
258 else if (r == 3 || r == 4)
260 AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID));
262 if (f != nullptr)
264 PluginWindow* const w = PluginWindow::getWindowFor (f, r == 4);
266 if (w != nullptr)
267 w->toFront (true);
273 void mouseDrag (const MouseEvent& e)
275 if (! e.mods.isPopupMenu())
277 Point<int> pos (originalPos + Point<int> (e.getDistanceFromDragStartX(), e.getDistanceFromDragStartY()));
279 if (getParentComponent() != nullptr)
280 pos = getParentComponent()->getLocalPoint (nullptr, pos);
282 graph.setNodePosition (filterID,
283 (pos.getX() + getWidth() / 2) / (double) getParentWidth(),
284 (pos.getY() + getHeight() / 2) / (double) getParentHeight());
286 getGraphPanel()->updateComponents();
290 void mouseUp (const MouseEvent& e)
292 if (e.mouseWasClicked() && e.getNumberOfClicks() == 2)
294 const AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID));
296 if (f != nullptr)
298 PluginWindow* const w = PluginWindow::getWindowFor (f, false);
300 if (w != nullptr)
301 w->toFront (true);
304 else if (! e.mouseWasClicked())
306 graph.setChangedFlag (true);
310 bool hitTest (int x, int y)
312 for (int i = getNumChildComponents(); --i >= 0;)
313 if (getChildComponent(i)->getBounds().contains (x, y))
314 return true;
316 return x >= 3 && x < getWidth() - 6 && y >= pinSize && y < getHeight() - pinSize;
319 void paint (Graphics& g)
321 g.setColour (Colours::lightgrey);
323 const int x = 4;
324 const int y = pinSize;
325 const int w = getWidth() - x * 2;
326 const int h = getHeight() - pinSize * 2;
328 g.fillRect (x, y, w, h);
330 g.setColour (Colours::black);
331 g.setFont (font);
332 g.drawFittedText (getName(),
333 x + 4, y + 2, w - 8, h - 4,
334 Justification::centred, 2);
336 g.setColour (Colours::grey);
337 g.drawRect (x, y, w, h);
340 void resized()
342 for (int i = 0; i < getNumChildComponents(); ++i)
344 PinComponent* const pc = dynamic_cast <PinComponent*> (getChildComponent(i));
346 if (pc != nullptr)
348 const int total = pc->isInput ? numIns : numOuts;
349 const int index = pc->index == FilterGraph::midiChannelNumber ? (total - 1) : pc->index;
351 pc->setBounds (proportionOfWidth ((1 + index) / (total + 1.0f)) - pinSize / 2,
352 pc->isInput ? 0 : (getHeight() - pinSize),
353 pinSize, pinSize);
358 void getPinPos (const int index, const bool isInput, float& x, float& y)
360 for (int i = 0; i < getNumChildComponents(); ++i)
362 PinComponent* const pc = dynamic_cast <PinComponent*> (getChildComponent(i));
364 if (pc != nullptr && pc->index == index && isInput == pc->isInput)
366 x = getX() + pc->getX() + pc->getWidth() * 0.5f;
367 y = getY() + pc->getY() + pc->getHeight() * 0.5f;
368 break;
373 void update()
375 const AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID));
377 if (f == nullptr)
379 delete this;
380 return;
383 numIns = f->getProcessor()->getNumInputChannels();
384 if (f->getProcessor()->acceptsMidi())
385 ++numIns;
387 numOuts = f->getProcessor()->getNumOutputChannels();
388 if (f->getProcessor()->producesMidi())
389 ++numOuts;
391 int w = 100;
392 int h = 60;
394 w = jmax (w, (jmax (numIns, numOuts) + 1) * 20);
396 const int textWidth = font.getStringWidth (f->getProcessor()->getName());
397 w = jmax (w, 16 + jmin (textWidth, 300));
398 if (textWidth > 300)
399 h = 100;
401 setSize (w, h);
403 setName (f->getProcessor()->getName());
406 double x, y;
407 graph.getNodePosition (filterID, x, y);
408 setCentreRelative ((float) x, (float) y);
411 if (numIns != numInputs || numOuts != numOutputs)
413 numInputs = numIns;
414 numOutputs = numOuts;
416 deleteAllChildren();
418 int i;
419 for (i = 0; i < f->getProcessor()->getNumInputChannels(); ++i)
420 addAndMakeVisible (new PinComponent (graph, filterID, i, true));
422 if (f->getProcessor()->acceptsMidi())
423 addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, true));
425 for (i = 0; i < f->getProcessor()->getNumOutputChannels(); ++i)
426 addAndMakeVisible (new PinComponent (graph, filterID, i, false));
428 if (f->getProcessor()->producesMidi())
429 addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, false));
431 resized();
435 FilterGraph& graph;
436 const uint32 filterID;
437 int numInputs, numOutputs;
439 private:
440 int pinSize;
441 Point<int> originalPos;
442 Font font;
443 int numIns, numOuts;
444 DropShadowEffect shadow;
446 GraphEditorPanel* getGraphPanel() const noexcept
448 return findParentComponentOfClass ((GraphEditorPanel*) nullptr);
451 FilterComponent (const FilterComponent&);
452 FilterComponent& operator= (const FilterComponent&);
455 //==============================================================================
456 class ConnectorComponent : public Component,
457 public SettableTooltipClient
459 public:
460 ConnectorComponent (FilterGraph& graph_)
461 : sourceFilterID (0),
462 destFilterID (0),
463 sourceFilterChannel (0),
464 destFilterChannel (0),
465 graph (graph_),
466 lastInputX (0),
467 lastInputY (0),
468 lastOutputX (0),
469 lastOutputY (0)
471 setAlwaysOnTop (true);
474 ~ConnectorComponent()
478 void setInput (const uint32 sourceFilterID_, const int sourceFilterChannel_)
480 if (sourceFilterID != sourceFilterID_ || sourceFilterChannel != sourceFilterChannel_)
482 sourceFilterID = sourceFilterID_;
483 sourceFilterChannel = sourceFilterChannel_;
484 update();
488 void setOutput (const uint32 destFilterID_, const int destFilterChannel_)
490 if (destFilterID != destFilterID_ || destFilterChannel != destFilterChannel_)
492 destFilterID = destFilterID_;
493 destFilterChannel = destFilterChannel_;
494 update();
498 void dragStart (int x, int y)
500 lastInputX = (float) x;
501 lastInputY = (float) y;
502 resizeToFit();
505 void dragEnd (int x, int y)
507 lastOutputX = (float) x;
508 lastOutputY = (float) y;
509 resizeToFit();
512 void update()
514 float x1, y1, x2, y2;
515 getPoints (x1, y1, x2, y2);
517 if (lastInputX != x1
518 || lastInputY != y1
519 || lastOutputX != x2
520 || lastOutputY != y2)
522 resizeToFit();
526 void resizeToFit()
528 float x1, y1, x2, y2;
529 getPoints (x1, y1, x2, y2);
531 const Rectangle<int> newBounds ((int) jmin (x1, x2) - 4,
532 (int) jmin (y1, y2) - 4,
533 (int) fabsf (x1 - x2) + 8,
534 (int) fabsf (y1 - y2) + 8);
536 if (newBounds != getBounds())
537 setBounds (newBounds);
538 else
539 resized();
541 repaint();
544 void getPoints (float& x1, float& y1, float& x2, float& y2) const
546 x1 = lastInputX;
547 y1 = lastInputY;
548 x2 = lastOutputX;
549 y2 = lastOutputY;
551 GraphEditorPanel* const hostPanel = getGraphPanel();
553 if (hostPanel != nullptr)
555 FilterComponent* srcFilterComp = hostPanel->getComponentForFilter (sourceFilterID);
557 if (srcFilterComp != nullptr)
558 srcFilterComp->getPinPos (sourceFilterChannel, false, x1, y1);
560 FilterComponent* dstFilterComp = hostPanel->getComponentForFilter (destFilterID);
562 if (dstFilterComp != nullptr)
563 dstFilterComp->getPinPos (destFilterChannel, true, x2, y2);
567 void paint (Graphics& g)
569 if (sourceFilterChannel == FilterGraph::midiChannelNumber
570 || destFilterChannel == FilterGraph::midiChannelNumber)
572 g.setColour (Colours::red);
574 else
576 g.setColour (Colours::green);
579 g.fillPath (linePath);
582 bool hitTest (int x, int y)
584 if (hitPath.contains ((float) x, (float) y))
586 double distanceFromStart, distanceFromEnd;
587 getDistancesFromEnds (x, y, distanceFromStart, distanceFromEnd);
589 // avoid clicking the connector when over a pin
590 return distanceFromStart > 7.0 && distanceFromEnd > 7.0;
593 return false;
596 void mouseDown (const MouseEvent&)
598 dragging = false;
601 void mouseDrag (const MouseEvent& e)
603 if ((! dragging) && ! e.mouseWasClicked())
605 dragging = true;
607 graph.removeConnection (sourceFilterID, sourceFilterChannel, destFilterID, destFilterChannel);
609 double distanceFromStart, distanceFromEnd;
610 getDistancesFromEnds (e.x, e.y, distanceFromStart, distanceFromEnd);
611 const bool isNearerSource = (distanceFromStart < distanceFromEnd);
613 getGraphPanel()->beginConnectorDrag (isNearerSource ? 0 : sourceFilterID,
614 sourceFilterChannel,
615 isNearerSource ? destFilterID : 0,
616 destFilterChannel,
619 else if (dragging)
621 getGraphPanel()->dragConnector (e);
625 void mouseUp (const MouseEvent& e)
627 if (dragging)
628 getGraphPanel()->endDraggingConnector (e);
631 void resized()
633 float x1, y1, x2, y2;
634 getPoints (x1, y1, x2, y2);
636 lastInputX = x1;
637 lastInputY = y1;
638 lastOutputX = x2;
639 lastOutputY = y2;
641 x1 -= getX();
642 y1 -= getY();
643 x2 -= getX();
644 y2 -= getY();
646 linePath.clear();
647 linePath.startNewSubPath (x1, y1);
648 linePath.cubicTo (x1, y1 + (y2 - y1) * 0.33f,
649 x2, y1 + (y2 - y1) * 0.66f,
650 x2, y2);
652 PathStrokeType wideStroke (8.0f);
653 wideStroke.createStrokedPath (hitPath, linePath);
655 PathStrokeType stroke (2.5f);
656 stroke.createStrokedPath (linePath, linePath);
658 const float arrowW = 5.0f;
659 const float arrowL = 4.0f;
661 Path arrow;
662 arrow.addTriangle (-arrowL, arrowW,
663 -arrowL, -arrowW,
664 arrowL, 0.0f);
666 arrow.applyTransform (AffineTransform::identity
667 .rotated (float_Pi * 0.5f - (float) atan2 (x2 - x1, y2 - y1))
668 .translated ((x1 + x2) * 0.5f,
669 (y1 + y2) * 0.5f));
671 linePath.addPath (arrow);
672 linePath.setUsingNonZeroWinding (true);
675 uint32 sourceFilterID, destFilterID;
676 int sourceFilterChannel, destFilterChannel;
678 private:
679 FilterGraph& graph;
680 float lastInputX, lastInputY, lastOutputX, lastOutputY;
681 Path linePath, hitPath;
682 bool dragging;
684 GraphEditorPanel* getGraphPanel() const noexcept
686 return findParentComponentOfClass ((GraphEditorPanel*) nullptr);
689 void getDistancesFromEnds (int x, int y, double& distanceFromStart, double& distanceFromEnd) const
691 float x1, y1, x2, y2;
692 getPoints (x1, y1, x2, y2);
694 distanceFromStart = juce_hypot (x - (x1 - getX()), y - (y1 - getY()));
695 distanceFromEnd = juce_hypot (x - (x2 - getX()), y - (y2 - getY()));
698 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectorComponent);
702 //==============================================================================
703 GraphEditorPanel::GraphEditorPanel (FilterGraph& graph_)
704 : graph (graph_),
705 draggingConnector (nullptr)
707 graph.addChangeListener (this);
708 setOpaque (true);
711 GraphEditorPanel::~GraphEditorPanel()
713 graph.removeChangeListener (this);
714 deleteAndZero (draggingConnector);
715 deleteAllChildren();
718 void GraphEditorPanel::paint (Graphics& g)
720 g.fillAll (Colours::white);
723 void GraphEditorPanel::mouseDown (const MouseEvent& e)
725 if (e.mods.isPopupMenu())
727 PopupMenu m;
729 MainHostWindow* const mainWindow = findParentComponentOfClass ((MainHostWindow*) nullptr);
731 if (mainWindow != nullptr)
733 mainWindow->addPluginsToMenu (m);
735 const int r = m.show();
737 createNewPlugin (mainWindow->getChosenType (r), e.x, e.y);
742 void GraphEditorPanel::createNewPlugin (const PluginDescription* desc, int x, int y)
744 graph.addFilter (desc, x / (double) getWidth(), y / (double) getHeight());
747 FilterComponent* GraphEditorPanel::getComponentForFilter (const uint32 filterID) const
749 for (int i = getNumChildComponents(); --i >= 0;)
751 FilterComponent* const fc = dynamic_cast <FilterComponent*> (getChildComponent (i));
753 if (fc != nullptr && fc->filterID == filterID)
754 return fc;
757 return nullptr;
760 ConnectorComponent* GraphEditorPanel::getComponentForConnection (const AudioProcessorGraph::Connection& conn) const
762 for (int i = getNumChildComponents(); --i >= 0;)
764 ConnectorComponent* const c = dynamic_cast <ConnectorComponent*> (getChildComponent (i));
766 if (c != nullptr
767 && c->sourceFilterID == conn.sourceNodeId
768 && c->destFilterID == conn.destNodeId
769 && c->sourceFilterChannel == conn.sourceChannelIndex
770 && c->destFilterChannel == conn.destChannelIndex)
772 return c;
776 return nullptr;
779 PinComponent* GraphEditorPanel::findPinAt (const int x, const int y) const
781 for (int i = getNumChildComponents(); --i >= 0;)
783 FilterComponent* const fc = dynamic_cast <FilterComponent*> (getChildComponent (i));
785 if (fc != nullptr)
787 PinComponent* const pin
788 = dynamic_cast <PinComponent*> (fc->getComponentAt (x - fc->getX(),
789 y - fc->getY()));
791 if (pin != nullptr)
792 return pin;
796 return nullptr;
799 void GraphEditorPanel::resized()
801 updateComponents();
804 void GraphEditorPanel::changeListenerCallback (ChangeBroadcaster*)
806 updateComponents();
809 void GraphEditorPanel::updateComponents()
811 int i;
812 for (i = getNumChildComponents(); --i >= 0;)
814 FilterComponent* const fc = dynamic_cast <FilterComponent*> (getChildComponent (i));
816 if (fc != nullptr)
817 fc->update();
820 for (i = getNumChildComponents(); --i >= 0;)
822 ConnectorComponent* const cc = dynamic_cast <ConnectorComponent*> (getChildComponent (i));
824 if (cc != nullptr && cc != draggingConnector)
826 if (graph.getConnectionBetween (cc->sourceFilterID, cc->sourceFilterChannel,
827 cc->destFilterID, cc->destFilterChannel) == nullptr)
829 delete cc;
831 else
833 cc->update();
838 for (i = graph.getNumFilters(); --i >= 0;)
840 const AudioProcessorGraph::Node::Ptr f (graph.getNode (i));
842 if (getComponentForFilter (f->nodeId) == 0)
844 FilterComponent* const comp = new FilterComponent (graph, f->nodeId);
845 addAndMakeVisible (comp);
846 comp->update();
850 for (i = graph.getNumConnections(); --i >= 0;)
852 const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
854 if (getComponentForConnection (*c) == 0)
856 ConnectorComponent* const comp = new ConnectorComponent (graph);
857 addAndMakeVisible (comp);
859 comp->setInput (c->sourceNodeId, c->sourceChannelIndex);
860 comp->setOutput (c->destNodeId, c->destChannelIndex);
865 void GraphEditorPanel::beginConnectorDrag (const uint32 sourceFilterID, const int sourceFilterChannel,
866 const uint32 destFilterID, const int destFilterChannel,
867 const MouseEvent& e)
869 delete draggingConnector;
870 draggingConnector = dynamic_cast <ConnectorComponent*> (e.originalComponent);
872 if (draggingConnector == nullptr)
873 draggingConnector = new ConnectorComponent (graph);
875 draggingConnector->setInput (sourceFilterID, sourceFilterChannel);
876 draggingConnector->setOutput (destFilterID, destFilterChannel);
878 addAndMakeVisible (draggingConnector);
879 draggingConnector->toFront (false);
881 dragConnector (e);
884 void GraphEditorPanel::dragConnector (const MouseEvent& e)
886 const MouseEvent e2 (e.getEventRelativeTo (this));
888 if (draggingConnector != nullptr)
890 draggingConnector->setTooltip (String::empty);
892 int x = e2.x;
893 int y = e2.y;
895 PinComponent* const pin = findPinAt (x, y);
897 if (pin != nullptr)
899 uint32 srcFilter = draggingConnector->sourceFilterID;
900 int srcChannel = draggingConnector->sourceFilterChannel;
901 uint32 dstFilter = draggingConnector->destFilterID;
902 int dstChannel = draggingConnector->destFilterChannel;
904 if (srcFilter == 0 && ! pin->isInput)
906 srcFilter = pin->filterID;
907 srcChannel = pin->index;
909 else if (dstFilter == 0 && pin->isInput)
911 dstFilter = pin->filterID;
912 dstChannel = pin->index;
915 if (graph.canConnect (srcFilter, srcChannel, dstFilter, dstChannel))
917 x = pin->getParentComponent()->getX() + pin->getX() + pin->getWidth() / 2;
918 y = pin->getParentComponent()->getY() + pin->getY() + pin->getHeight() / 2;
920 draggingConnector->setTooltip (pin->getTooltip());
924 if (draggingConnector->sourceFilterID == 0)
925 draggingConnector->dragStart (x, y);
926 else
927 draggingConnector->dragEnd (x, y);
931 void GraphEditorPanel::endDraggingConnector (const MouseEvent& e)
933 if (draggingConnector == nullptr)
934 return;
936 draggingConnector->setTooltip (String::empty);
938 const MouseEvent e2 (e.getEventRelativeTo (this));
940 uint32 srcFilter = draggingConnector->sourceFilterID;
941 int srcChannel = draggingConnector->sourceFilterChannel;
942 uint32 dstFilter = draggingConnector->destFilterID;
943 int dstChannel = draggingConnector->destFilterChannel;
945 deleteAndZero (draggingConnector);
947 PinComponent* const pin = findPinAt (e2.x, e2.y);
949 if (pin != nullptr)
951 if (srcFilter == 0)
953 if (pin->isInput)
954 return;
956 srcFilter = pin->filterID;
957 srcChannel = pin->index;
959 else
961 if (! pin->isInput)
962 return;
964 dstFilter = pin->filterID;
965 dstChannel = pin->index;
968 graph.addConnection (srcFilter, srcChannel, dstFilter, dstChannel);
973 //==============================================================================
974 class TooltipBar : public Component,
975 private Timer
977 public:
978 TooltipBar()
980 startTimer (100);
983 void paint (Graphics& g)
985 g.setFont (getHeight() * 0.7f, Font::bold);
986 g.setColour (Colours::black);
987 g.drawFittedText (tip, 10, 0, getWidth() - 12, getHeight(), Justification::centredLeft, 1);
990 void timerCallback()
992 Component* const underMouse = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
993 TooltipClient* const ttc = dynamic_cast <TooltipClient*> (underMouse);
995 String newTip;
997 if (ttc != nullptr && ! (underMouse->isMouseButtonDown() || underMouse->isCurrentlyBlockedByAnotherModalComponent()))
998 newTip = ttc->getTooltip();
1000 if (newTip != tip)
1002 tip = newTip;
1003 repaint();
1007 private:
1008 String tip;
1010 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TooltipBar);
1013 //==============================================================================
1014 GraphDocumentComponent::GraphDocumentComponent (AudioDeviceManager* deviceManager_)
1015 : deviceManager (deviceManager_)
1017 addAndMakeVisible (graphPanel = new GraphEditorPanel (graph));
1019 graphPlayer.setProcessor (&graph.getGraph());
1021 keyState.addListener (&graphPlayer.getMidiMessageCollector());
1023 addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (keyState,
1024 MidiKeyboardComponent::horizontalKeyboard));
1026 addAndMakeVisible (statusBar = new TooltipBar());
1028 deviceManager->addAudioCallback (&graphPlayer);
1029 deviceManager->addMidiInputCallback (String::empty, &graphPlayer.getMidiMessageCollector());
1031 graphPanel->updateComponents();
1034 GraphDocumentComponent::~GraphDocumentComponent()
1036 deviceManager->removeAudioCallback (&graphPlayer);
1037 deviceManager->removeMidiInputCallback (String::empty, &graphPlayer.getMidiMessageCollector());
1039 deleteAllChildren();
1041 graphPlayer.setProcessor (nullptr);
1042 keyState.removeListener (&graphPlayer.getMidiMessageCollector());
1044 graph.clear();
1047 void GraphDocumentComponent::resized()
1049 const int keysHeight = 60;
1050 const int statusHeight = 20;
1052 graphPanel->setBounds (0, 0, getWidth(), getHeight() - keysHeight);
1053 statusBar->setBounds (0, getHeight() - keysHeight - statusHeight, getWidth(), statusHeight);
1054 keyboardComp->setBounds (0, getHeight() - keysHeight, getWidth(), keysHeight);
1057 void GraphDocumentComponent::createNewPlugin (const PluginDescription* desc, int x, int y)
1059 graphPanel->createNewPlugin (desc, x, y);